// Copyright (C) Kumo inc. and its affiliates.
// Author: Jeff.li lijippy@163.com
// All rights reserved.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.
//

#pragma once

#include <type_traits>
#include <initializer_list>

namespace cantor::common::detail {
    // Checks whether T appears in Tys.
    template<class T, class... Tys>
    struct contains;

    template<class T>
    struct contains<T> : std::false_type {
    };

    template<class T, class Ty, class... Tys>
    struct contains<T, Ty, Tys...>
            : std::conditional<std::is_same<Ty, T>::value, std::true_type,
                contains<T, Tys...> >::type {
    };
} // namespace cantor::common::detail

namespace cantor::common {
    template<typename... Archs>
    struct TypeList {
        template<typename Arch>
        using add = TypeList<Archs..., Arch>;

        template<typename... OtherArchs>
        using extend = TypeList<Archs..., OtherArchs...>;

        template<typename Arch>
        static constexpr bool contains() noexcept {
            return detail::contains<Arch, Archs...>::value;
        }

        template<typename F>
        static void for_each(F &&f) noexcept {
            (void) std::initializer_list<bool>{(f(Archs{}), true)...};
        }
    };

    template<template<typename> class Pred, bool Unique, typename L, typename... Archs>
    struct FilterHelper;

    template<template<typename> class Pred, bool Unique, typename L>
    struct FilterHelper<Pred, Unique, L, TypeList<> > {
        using type = L;
    };
    /// Pred should be inh from std:: false or true,
    /// and template argument is Arch
    template<template<typename> class Pred, typename L, typename Arch, typename... Archs>
    struct FilterHelper<Pred, true, L, TypeList<Arch, Archs...> >
            : FilterHelper<Pred, true,
                typename std::conditional<(Pred<Arch>::value && !L::template contains<Arch>()),
                    typename L::template add<Arch>, L>::type,
                TypeList<Archs...> > {
    };

    /// Pred should be inh from std:: false or true,
    /// and template argument is Arch
    template<template<typename> class Pred, typename L, typename Arch, typename... Archs>
    struct FilterHelper<Pred, false, L, TypeList<Arch, Archs...> >
            : FilterHelper<Pred, false,
                typename std::conditional<(Pred<Arch>::value),
                    typename L::template add<Arch>, L>::type,
                TypeList<Archs...> > {
    };

    template<template<typename> class Pred,typename... Archs>
    struct FilteredList : FilterHelper<Pred, false, TypeList<>, Archs...> {};

 	template<template<typename> class Pred,typename... Archs>
    struct FilteredUniqueList : FilterHelper<Pred, true, TypeList<>, Archs...> {};

    template<class L, class... Archs>
    struct ExtractHelper;

    template<class L>
    struct ExtractHelper<L, TypeList<> > {
        using type = L;
    };

    template<class L, class Arch, class... Archs>
    struct ExtractHelper<L, TypeList<Arch, Archs...> >
            : ExtractHelper<typename L::template add<typename Arch::type>,
                TypeList<Archs...> > {
    };

    template<class... Archs>
    struct ExtractList : ExtractHelper<TypeList<>, Archs...>{};

	// Joins all TypeList Archs in a single TypeList.
    template <class... Archs>
    struct JoinList;

    template <class Arch>
    struct JoinList<Arch>
    {
        using type = Arch;
    };

    template <class Arch, class... Archs, class... Args>
    struct JoinList<Arch, TypeList<Archs...>, Args...>
        : JoinList<typename Arch::template extend<Archs...>, Args...>
    {
    };

    template<typename Visitor, typename L>
    struct IsInList {
        static constexpr bool value = L::template contains<Visitor>();
    };

	//////////////////////////////////////////////////////////////////////
	/// struct check_cpu_has_intruction_set {
    ///		template <class Arch>
    ///		void operator()(Arch arch) const
    /// 	{
    ///			do something
    ///		}
	/// };
	//////////////////////////////////////////////////////////////////////
} // namespace cantor::common
